//
// Copyright (c) 2011 Alex Nesterenko
//

#include "nane/InputSystem/X11/X11InputSystem.h"
#include <X11/keysym.h>
#include <X11/Xutil.h>
#include "nane/core/log.h"

namespace nane
{
    ////////////////////////////////////////////////////////////////////////////////////////////////
    X11InputSystem::X11InputSystem()
        : m_display( NULL )
        , m_hWindow( 0 )
    {

    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    X11InputSystem::~X11InputSystem()
    {

    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool X11InputSystem::initialize()
    {
        //X Key Map to KeyCode
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_1, EKeyCode::Num1));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_2, EKeyCode::Num2));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_3, EKeyCode::Num3));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_4, EKeyCode::Num4));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_5, EKeyCode::Num5));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_6, EKeyCode::Num6));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_7, EKeyCode::Num7));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_8, EKeyCode::Num8));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_9, EKeyCode::Num9));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_0, EKeyCode::Num0));

        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_BackSpace, EKeyCode::Back));

        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_minus, EKeyCode::Minus));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_equal, EKeyCode::Equals));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_space, EKeyCode::Space));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_comma, EKeyCode::Comma));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_period, EKeyCode::Period));

        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_backslash, EKeyCode::Backslash));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_slash, EKeyCode::Slash));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_bracketleft, EKeyCode::LBracket));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_bracketright, EKeyCode::RBracket));

        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_Escape, EKeyCode::Escape));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_Caps_Lock, EKeyCode::Capital));

        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_Tab, EKeyCode::Tab));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_Return, EKeyCode::Return));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_Control_L, EKeyCode::LControl));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_Control_R, EKeyCode::RControl));

        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_colon, EKeyCode::Colon));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_semicolon, EKeyCode::Semicolon));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_apostrophe, EKeyCode::Apostrophe));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_grave, EKeyCode::Grave));

        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_b, EKeyCode::B));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_a, EKeyCode::A));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_c, EKeyCode::C));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_d, EKeyCode::D));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_e, EKeyCode::E));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_f, EKeyCode::F));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_g, EKeyCode::G));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_h, EKeyCode::H));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_i, EKeyCode::I));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_j, EKeyCode::J));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_k, EKeyCode::K));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_l, EKeyCode::L));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_m, EKeyCode::M));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_n, EKeyCode::N));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_o, EKeyCode::O));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_p, EKeyCode::P));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_q, EKeyCode::Q));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_r, EKeyCode::R));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_s, EKeyCode::S));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_t, EKeyCode::T));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_u, EKeyCode::U));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_v, EKeyCode::V));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_w, EKeyCode::W));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_x, EKeyCode::X));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_y, EKeyCode::Y));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_z, EKeyCode::Z));

        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_F1, EKeyCode::F1));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_F2, EKeyCode::F2));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_F3, EKeyCode::F3));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_F4, EKeyCode::F4));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_F5, EKeyCode::F5));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_F6, EKeyCode::F6));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_F7, EKeyCode::F7));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_F8, EKeyCode::F8));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_F9, EKeyCode::F9));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_F10, EKeyCode::F10));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_F11, EKeyCode::F11));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_F12, EKeyCode::F12));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_F13, EKeyCode::F13));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_F14, EKeyCode::F14));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_F15, EKeyCode::F15));

        //Keypad
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_KP_0, EKeyCode::NumPad0));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_KP_1, EKeyCode::NumPad1));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_KP_2, EKeyCode::NumPad2));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_KP_3, EKeyCode::NumPad3));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_KP_4, EKeyCode::NumPad4));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_KP_5, EKeyCode::NumPad5));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_KP_6, EKeyCode::NumPad6));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_KP_7, EKeyCode::NumPad7));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_KP_8, EKeyCode::NumPad8));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_KP_9, EKeyCode::NumPad9));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_KP_Add, EKeyCode::Add));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_KP_Subtract, EKeyCode::Subtract));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_KP_Decimal, EKeyCode::Decimal));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_KP_Equal, EKeyCode::NumPadEquals));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_KP_Divide, EKeyCode::Divide));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_KP_Multiply, EKeyCode::Multiply));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_KP_Enter, EKeyCode::NumPadEnter));

        //Keypad with numlock off
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_KP_Home, EKeyCode::NumPad7));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_KP_Up, EKeyCode::NumPad8));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_KP_Page_Up, EKeyCode::NumPad9));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_KP_Left, EKeyCode::NumPad4));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_KP_Begin, EKeyCode::NumPad5));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_KP_Right, EKeyCode::NumPad6));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_KP_End, EKeyCode::NumPad1));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_KP_Down, EKeyCode::NumPad2));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_KP_Page_Down, EKeyCode::NumPad3));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_KP_Insert, EKeyCode::NumPad0));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_KP_Delete, EKeyCode::Decimal));

        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_Up, EKeyCode::Up));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_Down, EKeyCode::Down));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_Left, EKeyCode::Left));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_Right, EKeyCode::Right));

        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_Page_Up, EKeyCode::PgUp));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_Page_Down, EKeyCode::PgDown));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_Home, EKeyCode::Home));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_End, EKeyCode::End));

        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_Num_Lock, EKeyCode::NumLock));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_Print, EKeyCode::SYSRQ));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_Scroll_Lock, EKeyCode::Scroll));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_Pause, EKeyCode::Pause));

        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_Shift_R, EKeyCode::RShift));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_Shift_L, EKeyCode::LShift));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_Alt_R, EKeyCode::RMenu));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_Alt_L, EKeyCode::LMenu));

        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_Insert, EKeyCode::Insert));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_Delete, EKeyCode::Delete));

        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_Super_L, EKeyCode::LWIN));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_Super_R, EKeyCode::RWIN));
        m_XToKeyCodeMap.insert(TKeyCodeMap::value_type(XK_Menu, KC_APPS));    

        m_display = XOpenDisplay(NULL);
        return m_display != NULL;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void X11InputSystem::shutdown()
    {
        if( m_display != NULL )
        {
            XCloseDisplay(m_display);
            m_display = NULL;
        }
        m_XToKeyCodeMap.clear();
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool X11InputSystem::initializeContext( IWindow::OSHandle _windowHandle )
    {
        Window window = reinterpret_cast<Window>(_windowHandle);
        if( XSelectInput(m_display, window, KeyPressMask | KeyReleaseMask) == BadWindow )
        {
            return false;
        }
        m_hWindow = window;
        XGrabKeyboard(m_display, m_hWindow, True, GrabModeAsync, GrabModeAsync, CurrentTime);
        return true;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void X11InputSystem::shutdownContext()
    {
        XUngrabKeyboard(m_display, CurrentTime);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void X11InputSystem::update( IKeyboardInputHandler* _handler )
    {
        XEvent event;
        KeySym key;
        while( XPending(m_display) > 0 )
        {
            XNextEvent(m_display, &event);
            if( event.type == KeyPress && _handler != NULL )
            {
                event.xkey.state &= ~ShiftMask;
                event.xkey.state &= ~LockMask;
                XLookupString(&event.xkey, 0, 0,&key, 0);
                _handler->onKeyEvent(m_XToKeyCodeMap[key], true);
            }
            else if( event.type == KeyRelease && _handler != NULL )
            {
                event.xkey.state &= ~ShiftMask;
                event.xkey.state &= ~LockMask;
                XLookupString(&event.xkey, 0, 0,&key, 0);
                _handler->onKeyEvent(m_XToKeyCodeMap[key], false);
            }
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
}   // namespace nane
